//
// Created by yunnan xu on 2020/11/9.
//

#include "ShaderMgr.h"
#include "ResourceMgr.h"
#include "EngineCore.h"
#include "GLESUtils.h"
#include <EGL/egl.h>
#include <GLES3/gl32.h>

/*
 * Shader类型
 *      VertexShader,
        FragmentShader,
        GeometryShader,
        TessControlShader,
        TessEvaluateShader,
        ComputeShader,
 * */

char ShaderMgr::ShaderSuffix[6][12] = {
        "_vs.glsl",
        "_fs.glsl",
        "_gs.glsl",
        "_tcs.glsl",
        "_tes.glsl",
        "_cs.glsl",
};

int ShaderMgr::GLShaderEnum[6] = {
        GL_VERTEX_SHADER,
        GL_FRAGMENT_SHADER,
        GL_GEOMETRY_SHADER,
        GL_TESS_CONTROL_SHADER,
        GL_TESS_EVALUATION_SHADER,
        GL_COMPUTE_SHADER
};

std::string GLESShaderMgr::ShaderDictionaryPath = "shaders/";


GLESShaderMgr::GLESShaderMgr()
{

}

GLESShaderMgr::~GLESShaderMgr()
{

}

void GLESShaderMgr::LoadShaderList(const std::vector<std::string> & fileNameList)
{
    for (auto & filename : fileNameList)
    {
        std::string filepath = ShaderDictionaryPath + filename;
        long length = ResourceMgr::GetInstance()->GetFileLength(filepath.c_str(), AASSET_MODE_BUFFER);
        if (length <= 0)
        {
            LOGE("LoadShaderList GetFileLength filed filepath = %s, length = %d", filepath.c_str(), (int)length);
            continue;
        }

        if (shaderSources.find(filepath) != shaderSources.end())
        {
            LOGI("LoadShaderList shader %s already loaded", filepath.c_str());
            continue;
        }

        char * shader_source = new char[length + 1];
        memset(shader_source, '\0', length + 1);
        auto res_pair = shaderSources.emplace(filepath, shader_source);

        int read_res = ResourceMgr::GetInstance()->ReadFile(filepath.c_str(), (void *)(res_pair.first->second), length, AASSET_MODE_BUFFER);
        if (read_res < 0)
        {
            LOGE("LoadShaderList ReadFile error filepath = %s, res = %d", filepath.c_str(), read_res);
            continue;
        }
        else {
            LOGI("filepath = %s, read_res = %d", filepath.c_str(), read_res);
        }

        CreateAndCompileShader(filepath.c_str(), res_pair.first->second);
    }
}

bool GLESShaderMgr::CreateAndCompileShader(std::string file_path, const char * shader_source)
{

    if (shaderDict.find(file_path) != shaderDict.end())
    {
        LOGI("GLESShaderMgr::CreateAndCompileShader file_path = %s already exist", file_path.c_str());
        return false;
    }

    GLenum shader_enum = -1;

    for (int i = 0; i < ShaderType::Count; i++)
    {
        if (file_path.find(ShaderSuffix[i]) != std::string::npos)
        {
            shader_enum = GLShaderEnum[i];
            break;
        }
    }

    if (shader_enum < 0)
    {
        LOGI("GLESShaderMgr::CreateAndCompileShader file_path = %s suffix not valid", file_path.c_str());
        return false;
    }

    GLint shader = glCreateShader(shader_enum);

    if (shader == GL_NONE)
    {
        LOGE("GLESShaderMgr::CreateAndCompileShader create shader = 0");
        return false;
    }

    glShaderSource(shader, 1, &shader_source, NULL);
    glCompileShader(shader);

    GLint isCompiled = 0;
    glGetShaderiv(shader, GL_COMPILE_STATUS, &isCompiled);
    if (isCompiled == GL_FALSE)
    {
        GLint maxLength = 0;

        GLchar errorLog[1024];
        glGetShaderInfoLog(shader, 1024, &maxLength, errorLog);

        glDeleteShader(shader); // Don't leak the shader.

        LOGE("GLESShaderMgr::CreateAndCompileShader file_path = %s, error log : %s", file_path.c_str(), errorLog);

        return false;
    }

    shaderDict[file_path] = shader;
    return true;
}

GLuint GLESShaderMgr::GetShaderByPath(const std::string & path)
{
    if (shaderDict.find(path) == shaderDict.end()) {
        LOGE("GLESShaderMgr::GetShaderByPath cannot find shader file_path = %s", path.c_str());
        return 0;
    }

    return shaderDict[path];
}
